﻿using KzwgRoomTester;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;


class UDPConnection : IConnection
{
    private Socket mSocket = null;
    private KCP mKCP = null;
    //private Thread mThread;

    private ByteBuffer mRecvBuffer = ByteBuffer.Allocate(1024 * 32);
    private ByteBuffer mTempBuffer = ByteBuffer.Allocate(1024 * 32);
    private UInt32 mNextUpdateTime = 0;

    public bool IsConnected { get { return mSocket != null && mSocket.Connected; } }
    public bool WriteDelay { get; set; }

    private static FileLogger sendLog = new FileLogger("sendLog.log");

    private static FileLogger recveLog = new FileLogger("recv.log");

    public void Connect(string host, int port)
    {
        var endpoint = IPAddress.Parse(host);
        mSocket = new Socket(endpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
        mSocket.Connect(endpoint, port);
        //mKCP = new KCP((uint)(new Random().Next(1, Int32.MaxValue)), rawSend);
        mKCP = new KCP(Utils.IncreaseTick(), rawSend);
        // normal:  0, 40, 2, 1
        // fast:    0, 30, 2, 1
        // fast2:   1, 20, 2, 1
        // fast3:   1, 10, 2, 1
        //mKCP.NoDelay(1, 10, 2, 1);
        mKCP.NoDelay(1, 20, 2, 1);
        mKCP.WndSize(256, 256);
        mKCP.SetMtu(470);
        mRecvBuffer.Clear();
        mTempBuffer.Clear();
        //mThread = new Thread(update);
        //mThread.Start();
        //new Task(update).Start();
        TaskManager.AddTask(update, 10);
    }

    public void Close()
    {
        if (mSocket != null)
        {
            mSocket.Close();
            mSocket = null;
            mRecvBuffer.Clear();
        }
    }

    private void rawSend(byte[] data, int length)
    {
        if (dropPackage())
        {
            //sendLog.WriteLog(data, 0, length, "drop");
            return;
        }
        sendLog.WriteLog(data, 0, length, "send");
        sendLog.WriteLog(mKCP.HeadInfo(), "kcp");

        if (mSocket != null)
        {
            mSocket.Send(data, length, SocketFlags.None);
        }
    }



    public int Send(byte[] data, int index, int length)
    {
        if (mSocket == null)
            return -1;

        //Logger.LogInfo("send data: {0}", length);
        

        if (mKCP.WaitSnd >= mKCP.SndWnd)
        {
            return 0;
        }

        mNextUpdateTime = 0;

        var n = mKCP.Send(data, index, length);

        if (mKCP.WaitSnd >= mKCP.SndWnd || !WriteDelay)
        {
            mKCP.Flush(false);
        }
        return n;
    }

    Random random = new Random();
    private bool dropPackage()
    {
        return random.Next(0, 100) < 20;
    }


    public int RecvInBuffer()
    {

        if (mSocket == null)
            return -1;

        if (!mSocket.Poll(0, SelectMode.SelectRead))
        {
            return 0;
        }

        var rn  = mSocket.Receive(mTempBuffer.RawBuffer, mTempBuffer.WriterIndex, mTempBuffer.WritableBytes, SocketFlags.None);
        if (rn <= 0)
        {
            return rn;
        }
        if (dropPackage())
        {
            //recveLog.WriteLog(mTempBuffer.RawBuffer, 0, rn, "recv drop");
            mTempBuffer.Clear();
            return 0;
        }

        
        mTempBuffer.WriterIndex += rn;

        var inputN = mKCP.Input(mTempBuffer.RawBuffer, mTempBuffer.ReaderIndex, mTempBuffer.ReadableBytes, true, true);
        recveLog.WriteLog(mTempBuffer.RawBuffer, 0, rn, "recv");
        recveLog.WriteLog(mKCP.HeadInfo(), "kcp");
        if (inputN < 0)
        {
            mTempBuffer.Clear();
            return inputN;
        }
        mTempBuffer.Clear();

        // 读完所有完整的消息
        for (;;)
        {
            var size = mKCP.PeekSize();
            if (size <= 0) break;

            mRecvBuffer.EnsureWritableBytes(size);

            var n = mKCP.Recv(mRecvBuffer.RawBuffer, mRecvBuffer.WriterIndex, size);
            if (n > 0) mRecvBuffer.WriterIndex += n;
        }

        return mRecvBuffer.ReadableBytes;
    }



    //public int RecvLength(byte[] data, int index, int length)
    //{
    //    if (mSocket == null)
    //        return -1;

    //    if (!mSocket.Poll(0, SelectMode.SelectRead))
    //    {
    //        return 0;
    //    }

    //    var rn = 0;
    //    while (mRecvBuffer.ReadableBytes < length)
    //    {
    //        rn = mSocket.Receive(mTempBuffer.RawBuffer, mTempBuffer.WriterIndex, mTempBuffer.WritableBytes, SocketFlags.None);
    //        if (rn <= 0)
    //        {
    //            continue;
    //        }
    //        mTempBuffer.WriterIndex += rn;

    //        var inputN = mKCP.Input(mTempBuffer.RawBuffer, mTempBuffer.ReaderIndex, mTempBuffer.ReadableBytes, true, true);
    //        if (inputN < 0)
    //        {
    //            mTempBuffer.Clear();
    //            continue;
    //        }
    //        mTempBuffer.Clear();

    //        // 读完所有完整的消息
    //        while (true)
    //        {
    //            var size = mKCP.PeekSize();
    //            if (size <= 0) break;

    //            mRecvBuffer.EnsureWritableBytes(size);

    //            var n = mKCP.Recv(mRecvBuffer.RawBuffer, mRecvBuffer.WriterIndex, size);
    //            if (n > 0) mRecvBuffer.WriterIndex += n;
    //        }
    //    }

    //    Buffer.BlockCopy(mRecvBuffer.RawBuffer, mRecvBuffer.ReaderIndex, data, index, length);
    //    mRecvBuffer.ReaderIndex += length;
    //    // 重置读写指针
    //    if (mRecvBuffer.ReaderIndex == mRecvBuffer.WriterIndex)
    //    {
    //        mRecvBuffer.Clear();
    //    }
    //    else
    //    {
    //        mRecvBuffer.TrimReadedBytes();
    //    }
    //    return length;

    //}



    public int GetRecvLen()
    {
        return mRecvBuffer.ReadableBytes;
    }

    public int RecvData(byte[] data, int index, int length)
    {
        if (mRecvBuffer.ReadableBytes > 0)
        {
            var recvBytes = Math.Min(mRecvBuffer.ReadableBytes, length);
            Buffer.BlockCopy(mRecvBuffer.RawBuffer, mRecvBuffer.ReaderIndex, data, index, recvBytes);
            mRecvBuffer.ReaderIndex += recvBytes;
            // 重置读写指针
            if (mRecvBuffer.ReaderIndex == mRecvBuffer.WriterIndex)
            {
                mRecvBuffer.Clear();
            }
            else
            {
                mRecvBuffer.TrimReadedBytes();
            }
            return recvBytes;
        }
        return 0;

    }


    private void update()
    {
        while (IsConnected)
        {
            doUpdate();
            Thread.Sleep(10);
        }
    }

    private void doUpdate()
    {
        if (mSocket == null)
            return;

        if (0 == mNextUpdateTime || mKCP.CurrentMS >= mNextUpdateTime)
        {
            mKCP.Update();
            mNextUpdateTime = mKCP.Check();
        }
    }
}

